﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TwoPageView3 {
    class TpvPdfDirectoryInfo : ITpvDirectoryInfo {
        private DirectoryInfo mDirectoryInfo;
        private bool mExists;
        private readonly string mFullName;
        private readonly string mFileName;
        private string mTmpDirName = string.Empty;
        private static string mPrefixOfTmpFolderPath = string.Empty;
        private static Dictionary<string, TpvPdfDirectoryInfo> mPathNameToExtractedDirectoryInfo =
                new Dictionary<string, TpvPdfDirectoryInfo>();
        private static ReaderWriterLock mLock = new ReaderWriterLock();
        private DateTime mLastWriteTime;

        /// <summary>
        /// ctor for directory (or PDF file as directory)
        /// </summary>
        /// <param name="path"></param>
        private TpvPdfDirectoryInfo(string path) {
            mFullName = path;
            mFileName = Path.GetFileName(mFullName);
            mTmpDirName = FindExtractedDirectoryPath(mFullName);
            string translatedPathName = TranslateToExtractedFilePath(mFullName, true);
            //Console.WriteLine("D: TpvDirectoryInfo ctor {0} -> {1}", path, translatedPathName);
            if (!File.Exists(translatedPathName) || !translatedPathName.EndsWith(".pdf", true, CultureInfo.CurrentCulture)) {
                throw new ArgumentException(path);
            }
            mExists = true;
            mLastWriteTime = File.GetLastWriteTimeUtc(translatedPathName);
        }

        public static TpvPdfDirectoryInfo Factory(string path) {
            var x = FindExtractedDirectoryInfo(path, true);
            if (null == x || !x.mFullName.Equals(path)) {
                x = new TpvPdfDirectoryInfo(path);
            }
            return x;
        }

        public static void SetPrefixOfTmpFolderPath(string path, bool ifSetCustomPath) {
            mPrefixOfTmpFolderPath = ifSetCustomPath ? path : string.Empty;
        }

        public static string PrefixOfTmpFolderPath() {
            return (0 == mPrefixOfTmpFolderPath.Length) ?
                    Path.GetTempPath() : (mPrefixOfTmpFolderPath + Path.DirectorySeparatorChar);
        }

        private static TpvPdfDirectoryInfo FindExtractedDirectoryInfo(string path, bool matchWithExactSamePath) {
            int matchLen = 0;
            TpvPdfDirectoryInfo di = null;
            mLock.AcquireReaderLock(Timeout.Infinite);
            foreach (KeyValuePair<string, TpvPdfDirectoryInfo> kvp in mPathNameToExtractedDirectoryInfo) {
                if (!path.StartsWith(kvp.Key, StringComparison.Ordinal)) {
                    continue;
                }
                if (!matchWithExactSamePath && path.Equals(kvp.Key)) {
                    continue;
                }
                if (matchLen < kvp.Key.Length) {
                    matchLen = kvp.Key.Length;
                    di = kvp.Value;
                }
            }
            mLock.ReleaseReaderLock();
            return di;
        }

        // return pathname which contains actual zip file
        private static string TranslateToExtractedFilePath(string path, bool matchWithExactSamePath) {
            string result = path;
            var di = FindExtractedDirectoryInfo(path, matchWithExactSamePath);
            if (null != di && !path.Equals(di.FullName)) {
                result = result.Replace(di.FullName, di.TmpDirName);
            }
            return result;
        }

        private static string FindExtractedDirectoryPath(string path) {
            string result = string.Empty;
            mLock.AcquireReaderLock(Timeout.Infinite);
            if (mPathNameToExtractedDirectoryInfo.ContainsKey(path)) {
                result = mPathNameToExtractedDirectoryInfo[path].mTmpDirName;
            }
            mLock.ReleaseReaderLock();
            return result;
        }

        public static void RemoveTmpDirs() {
            mLock.AcquireWriterLock(Timeout.Infinite);
            //Console.WriteLine("D: RemoveTmpDirs() lastPageToDisplay={0}", mPathNameToExtractedDirectoryInfo.Count);
            foreach (KeyValuePair<string, TpvPdfDirectoryInfo> kvp in mPathNameToExtractedDirectoryInfo) {
                try {
                    //Console.WriteLine("D: RemoveTmpDirs() {0}", kvp.Value.mTmpDirName);
                    if (Directory.Exists(kvp.Value.TmpDirName)) {
                        Directory.Delete(kvp.Value.TmpDirName, true);
                    }
                } catch (IOException) {
                }
            }
            mPathNameToExtractedDirectoryInfo.Clear();
            mLock.ReleaseWriterLock();
        }

        public bool Exists {
            get { return mExists; }
        }

        public  bool HasSubDirectory() {
            return false;
        }

        public bool IsPdf() {
            return true;
        }

        public void Refresh() {
            if (null != mDirectoryInfo) {
                mDirectoryInfo.Refresh();
            }
        }

        public  string Name {
            get { return mFileName; }
        }

        public  string FullName {
            get { return mFullName; }
        }

        public string TmpDirName {
            get { return mTmpDirName; }
        }

        public IEnumerable<ITpvDirectoryInfo> EnumerateDirectories() {
            return Enumerable.Empty<ITpvDirectoryInfo>();
        }

        public  IEnumerable<TpvFileInfo> EnumerateImageFiles() {
            mLock.AcquireWriterLock(Timeout.Infinite);
            if (0 == mTmpDirName.Length) {
                mTmpDirName = TpvPdfDirectoryInfo.PrefixOfTmpFolderPath() + Path.GetRandomFileName();
                Directory.CreateDirectory(mTmpDirName);
                mDirectoryInfo = new DirectoryInfo(mTmpDirName);
                mPathNameToExtractedDirectoryInfo.Add(mFullName, this);
            }
            mLock.ReleaseWriterLock();

            var extractor = new TpvPdfExtractor();
            var translatedPathName = TranslateToExtractedFilePath(mFullName, true);

            return extractor.ExtractImages(translatedPathName, mTmpDirName);
        }
    }
}
